<template>
<div :class="[$style.container]" tabindex="0">
  <div style="position: relative;">
    <div :class="[$style.dragIcon]" ref="drag">
      <div>
        <svg-icon icon="docs" hidden></svg-icon>
        <svg-icon icon="file" hidden></svg-icon>
        <svg-icon icon="files" hidden></svg-icon>
      </div>
      <div :class="[$style.iconBadge]"></div>
    </div>
  </div>
  <context-menu ref="contextmenu">
    <template v-slot="{ data }">
      <context-menu-item @click.native="openDoc(data)" v-if="canOpen(data)">打开</context-menu-item>
      <context-menu-item @click.native="openDoc(data)" v-if="canPreview(data)">预览</context-menu-item>
      <context-menu-item @click.native="openWithLocal(data)" v-if="canOpenWithLocal(data)">本地程序打开</context-menu-item>
      <context-menu-item @click.native="onlineEditDoc(data)" v-if="canEdit(data)">在线编辑</context-menu-item>
      <context-menu-item @click.native="onAuthorize(data)" v-if="canAuthorize(data)">授权</context-menu-item>
      <context-menu-item @click.native="cpDoc(data)" v-if="canCopy(data)">复制(CTRL + C)</context-menu-item>
      <context-menu-item @click.native="mvDoc(data)" v-if="canMove(data)">移动(CTRL + X)</context-menu-item>
      <context-menu-item @click.native="downloadDoc(data)" v-if="canDownload(data)">下载</context-menu-item>
      <context-menu-item @click.native="renameDoc(data)" v-if="canRename(data)">重命名</context-menu-item>
      <context-menu-item @click.native="createDocLink(data)" v-if="canLink(data)">分享外链</context-menu-item>
      <context-menu-item @click.native="updateDocTags(data)" v-if="canTag(data)">打标签</context-menu-item>
      <el-dropdown-item @click.native="lockDoc(data)" v-if="canLock(data)">锁定</el-dropdown-item>
      <el-dropdown-item @click.native="unlockDoc(data)" v-if="canUnlock(data)">解锁</el-dropdown-item>
      <!-- <context-menu-item @click.native="publishDoc(data)">发布</context-menu-item> -->
      <context-menu-item @click.native="delDoc(data)" v-if="canDelete(data)"><span class="text-error">删除</span></context-menu-item>
      <context-menu-item @click.native="applyFlow(data)" v-if="canApplyFlow(data)">发起流程</context-menu-item>
    </template>
  </context-menu>
  <ul :class="[$style.list]"
    v-infinite-scroll="onLoad"
    :infinite-scroll-disabled="disabled"
    :infinite-scroll-immediate="false">
    <li :class="[$style.item, selectCls(row), checkedCls(row)]" v-for="(row) in data.rows" :key="row.id"
      @dblclick="openDoc(row)"
      draggable
      @dragover="e => dragHandlers.dragover(e, row)"
      @drop="e => dragHandlers.drop(e, row)"
      @dragstart="e => dragHandlers.dragstart(e, row)"
      @dragend="e => dragHandlers.dragend(e, row)"
      @contextmenu="e => onContextmenu(e, row)"
      @click.exact="onSelect(row)"
      @mousedown.alt.prevent="onAltCheck(row)"
      @mousedown.shift.prevent="onShiftCheck(row)">
      <div :class="[$style.col, $style.col0]" @click.stop="">
        <el-checkbox v-model="row.checked"></el-checkbox>
      </div>
      <div :class="[$style.col, $style.col1]">
        <div :class="[$style.docIcon]"><doc-icon :data="row"></doc-icon></div>
        <div :class="[$style.docContent]">
          <div :class="[$style.docText]">
            <span :underline="false" @click="openDoc(row)" :class="[$style.docTextContent]" :title="row.name">{{row.name}}</span>
            <span class="ml-2 text-error" :class="[$style.docTextAction]">
              <span v-if="row.locked">
                <el-tooltip :content="'锁定到 ' + row.lockTime">
                  <i class="el-icon-lock"></i>
                </el-tooltip>
              </span>
              <span class="text-error ml-1" v-if="row.status === 'published'"><i class="el-icon-s-promotion"></i></span>
              <span class="ml-2 animate-flicker" v-if="row.editing"><svg-icon icon="editing"></svg-icon></span>
            </span>
          </div>
          <div>
            <span><el-link :underline="false" @click="updateDocTags(row)"><svg-icon icon="tag"></svg-icon></el-link></span>
            <span>
              <el-tag size="mini" v-for="(tag, j) in row.tags " :key="j" class="ml-1" type="info" effect="plain" closable @close="removeDocTag(row, tag)">{{tag}}</el-tag>
            </span>
          </div>
        </div>
      </div>
      <div :class="[$style.col, $style.col2]">
        <span>{{row.modifiedTime}}</span><el-divider direction="vertical"></el-divider><span>{{row.modifiedByName}}</span>
      </div>
      <div :class="[$style.col, $style.col3]">{{row.size | size}}</div>
      <div :class="[$style.col, $style.col4]">
        <el-dropdown trigger="click">
          <span><el-link :underline="false"><i class="el-icon-more"></i></el-link></span>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item @click.native="openDoc(row)" v-if="canOpen(row)">打开</el-dropdown-item>
            <el-dropdown-item @click.native="openDoc(row)" v-if="canPreview(row)">预览</el-dropdown-item>
            <el-dropdown-item @click.native="onlineEditDoc(row)" v-if="canEdit(row)">在线编辑</el-dropdown-item>
            <el-dropdown-item @click.native="openWithLocal(row)" v-if="canOpenWithLocal(row)">本地程序打开</el-dropdown-item>
            <el-dropdown-item @click.native="onAuthorize(row)" v-if="canAuthorize(row)">授权</el-dropdown-item>
            <el-dropdown-item @click.native="cpDoc(row)" v-if="canCopy(row)">复制(CTRL + C)</el-dropdown-item>
            <el-dropdown-item @click.native="mvDoc(row)" v-if="canMove(row)">移动(CTRL + X)</el-dropdown-item>
            <el-dropdown-item @click.native="downloadDoc(row)" v-if="canDownload(row)">下载</el-dropdown-item>
            <el-dropdown-item @click.native="renameDoc(row)" v-if="canRename(row)">重命名</el-dropdown-item>
            <el-dropdown-item @click.native="createDocLink(row)" v-if="canLink(row)">分享外链</el-dropdown-item>
            <el-dropdown-item @click.native="updateDocTags(row)" v-if="canTag(row)">打标签</el-dropdown-item>
            <el-dropdown-item @click.native="lockDoc(row)" v-if="canLock(row)">锁定</el-dropdown-item>
            <el-dropdown-item @click.native="unlockDoc(row)" v-if="canUnlock(row)">解锁</el-dropdown-item>
            <!-- <el-dropdown-item @click.native="publishDoc(row)">发布</el-dropdown-item> -->
            <el-dropdown-item @click.native="delDoc(row)" v-if="canDelete(row)"><span class="text-error">删除</span></el-dropdown-item>
            <el-dropdown-item @click.native="applyFlow(row)" v-if="canApplyFlow(row)">发起流程</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
      <div :class="[$style.actions]">
        <span class="ml-2" @click.stop="">
          <el-link :underline="false" @click="createDocLink(row)" v-if="canLink(row)"><i class="el-icon-share ft-lg"></i></el-link>
        </span>
        <span class="ml-2" @click.stop="">
          <el-dropdown>
            <el-link :underline="false">
              <i class="el-icon-star-on ft-lg" v-if="row.starred"></i>
              <i class="el-icon-star-off ft-lg" v-else></i>
            </el-link>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item @click.native="onUnstar(row)" v-if="row.starred">取消收藏</el-dropdown-item>
              <el-dropdown-item v-for="(g, k) in starGroups" :key="g.id" :divided="k === 0" @click.native="onStar(row, g)">{{g.name}}</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </span>
        <span class="ml-2" @click="downloadDoc(row)" v-if="hasPerm('download', row)"><el-link :underline="false"><i class="el-icon-download ft-lg"></i></el-link></span>
      </div>
    </li>
  </ul>
  <p class="text-center text-secondary" v-if="data.loading">加载中...</p>
  <p class="text-center text-secondary" v-if="!data.hasMore && !data.loading && data.rows.length > 0">没有更多了</p>
</div>
</template>

<script lang="ts">

import { hasPerm, PermissionName } from '@/config/acl'
import { PageData } from '@/config/page'
import { Vue, Component, Provide, Watch, Ref, Prop, Inject, Emit } from 'vue-property-decorator'
import { canEdit, canPreview } from '@/config/content-type'
import { copyToClipboard } from '@/utils/copy'
import { cp, mv } from '@/api/doc'
import { queryGroups, star, unstarByDoc } from '@/api/star'

const sameRow = (r1: any, r2: any) => {
  return r1 === r2 || (!!r1?.id && r1?.id === r2?.id)
}

@Component
export default class DocTable extends Vue {
  @Prop() data!: PageData<any>

  @Prop() selectedRow!: any

  @Emit('update:selected-row') updateSelectedRow (row: any) {}

  @Ref() contextmenu!: any

  @Inject() openDoc!: (row: any) => void

  @Inject() openWithLocal!: (row: any) => void

  @Inject() onlineEditDoc!: (row: any) => void

  @Inject() downloadDoc!: (row?: any) => void

  @Inject() delDoc!: (row?: any) => void

  @Inject() mvDoc!: (row?: any) => void

  @Inject() mvDocToTarget!: (rows: any[], target: any) => void

  @Inject() cpDoc!: (row?: any) => void

  @Inject() renameDoc!: (row?: any) => void

  @Inject() selectRow!: (row: any) => void

  @Inject() publishDoc!: (row: any) => void

  @Inject() updateDocTags!: (row: any) => void

  @Inject() removeDocTag!: (row: any, tag: string) => void

  @Inject() lockDoc!: (row?: any) => void

  @Inject() unlockDoc!: (row?: any) => void

  @Inject() createDocLink!: (row: any) => void

  @Inject() onAuthorize!: (row: any) => void

  @Inject() applyFlow!: (row: any) => void

  starGroups: any[] = []

  dragRows: any = []

  dragHandlers = {
    dragover: (e: DragEvent, row: any) => {
      if (row.type === 'dir' && !this.dragRows.some((v: any) => v.id === row.id)) {
        e.preventDefault()
      }
    },
    drop: (e: DragEvent, row: any) => {
      console.log('drop', this.dragRows)
      if (this.dragRows.length > 0 && !this.dragRows.some((v: any) => v.id === row.id)) {
        e.preventDefault()
        // this.rows = this.rows.filter(v => {
        //   return !this.dragRows.some((w: any) => sameRow(w, v))
        // })
        // console.log(this.rows.length)
        // this.rows.pop()
        this.mvDocToTarget(this.dragRows, row)
      }
    },
    dragstart: (e: DragEvent, row: any) => {
      document.body.setAttribute('dragging', 'dragging')
      const checkedRows = this.checkedRows
      if (checkedRows.some(v => sameRow(v, row))) this.dragRows = this.checkedRows.slice()
      else this.dragRows = [row]
      const dt = e.dataTransfer as DataTransfer
      const ele = this.$refs.drag as HTMLDivElement
      ;(ele.lastElementChild as any).textContent = this.dragRows.length
      const eleIconWrap = (ele.firstElementChild as HTMLDivElement)
      const idx = this.dragRows.length > 1 ? 2 : (this.dragRows[0].type === 'dir' ? 0 : 1)
      for (let i = 0; i < eleIconWrap.children.length; i++) {
        if (idx === i) (eleIconWrap.children[i] as HTMLDivElement).style.display = 'block'
        else (eleIconWrap.children[i] as HTMLDivElement).style.display = 'none'
      }
      dt.setDragImage(ele, 0, 0)
    },
    dragend: (e: DragEvent, row: any) => {
      document.body.removeAttribute('dragging')
      this.dragRows = []
    }
  }

  get current () {
    return this.selectedRow
  }

  get disabled () {
    return this.data.loading || !this.data.hasMore
  }

  get checkedRows () {
    const rows = this.data.rows
    return rows.filter(v => v.checked)
  }

  checkedCls (row: any) {
    return row.checked ? this.$style.checked : ''
  }

  selectCls (row: any) {
    return sameRow(row, this.current) ? this.$style.selected : ''
  }

  hasPerm (name: PermissionName, ...rows: any[]) {
    return hasPerm(name, ...rows)
  }

  canMove (row?: any) {
    const rows = row ? [row] : this.checkedRows
    return !rows.some(v => v.locked) && hasPerm('move', ...rows) && !rows.some(v => v.teamDir)
  }

  canRename (row?: any) {
    if (!row) return false
    const rows = row ? [row] : this.checkedRows
    return !rows.some(v => v.locked) && hasPerm('rename', ...rows) && !rows.some(v => v.teamDir)
  }

  canDelete (row?: any) {
    const rows = row ? [row] : this.checkedRows
    return !rows.some(v => v.locked) && hasPerm('delete', ...rows) && !rows.some(v => v.teamDir)
  }

  canCopy (row?: any) {
    const rows = row ? [row] : this.checkedRows
    return hasPerm('copy', ...rows)
  }

  canDownload (row?: any) {
    const rows = row ? [row] : this.checkedRows
    return hasPerm('download', ...rows)
  }

  canLock (row?: any) {
    if (!row) return false
    return this.hasPerm('upload', row) && row.type === 'file' && !row.locked
  }

  canUnlock (row?: any) {
    if (!row) return false
    return row.type === 'file' && (row?.privilege?.hasPrivilege || row.lockedBy === this.$auth.principle?.user?.id) && row.locked
  }

  canPreview (row?: any) {
    if (!row) return false
    return row.type === 'file' && this.hasPerm('preview', row) && canPreview(row.name)
  }

  canOpenWithLocal (row?: any) {
    if (!row) return false
    return row.type === 'file' && this.hasPerm('download', row)
  }

  canOpen (row?: any) {
    if (!row) return false
    return row.type === 'dir'
  }

  canTag (row?: any) {
    return true
  }

  canEdit (row?: any) {
    if (!row) return false
    return this.hasPerm('edit', row) && canEdit(row.name)
  }

  canLink (row?: any) {
    if (!row) return false
    return this.hasPerm('link', row)
  }

  canAuthorize (row?: any) {
    const ent = row?.ownType === 'enterprise'
    if (!row || !ent) return false
    // if (row.type !== 'dir') return false
    return !!row.privilege || this.hasPerm('authorize', row)
  }

  canApplyFlow (row?: any) {
    const ent = row?.ownType === 'enterprise'
    if (!row || !ent) return false
    return true
  }

  onLoad () {
    this.data.loadNext()
  }

  onCheckAll (e?: Event) {
    console.log(e)
    if (e) e.preventDefault()
    this.data.rows.forEach(v => {
      v.checked = true
    })
  }

  onSelect (row: any) {
    this.updateSelectedRow(row || null)
  }

  onShiftCheck (row: any) {
    const rows = this.data.rows
    const start = this.current || rows[0]
    if (!start) return
    const s = rows.findIndex(v => sameRow(v, start))
    const e = rows.findIndex(v => sameRow(v, row))
    if (s < 0 || e < 0) return
    const min = Math.min(s, e)
    const max = Math.max(s, e)
    for (let i = min; i < rows.length; i++) {
      if (i >= min && i <= max) rows[i].checked = true
      else rows[i].checked = false
    }
  }

  onAltCheck (row: any) {
    if (row.checked) row.checked = false
    else row.checked = true
  }

  onContextmenu (e: MouseEvent, row: any) {
    const batch = this.checkedRows.length > 1 && this.checkedRows.some(v => v.id === row.id)
    this.contextmenu.open(e, batch ? undefined : row)
  }

  onShortcutCopy (type: 'copy' | 'move' = 'copy') {
    const data = this.checkedRows.length > 0 ? this.checkedRows : (this.current ? [this.current] : null)
    if (!data) return
    const text = {
      type,
      data: data.map((v: any) => v.id)
    }
    const content = 'Rawa_File_CpMv:' + btoa(JSON.stringify(text))
    copyToClipboard(content)
    this.$message.info(`已${type === 'move' ? '剪切' : '复制'}${data.length}项到粘贴板`)
  }

  onUnstar (row: any) {
    unstarByDoc(row.id).then(() => {
      Object.assign(row, { starred: true })
      this.$message.success('已取消收藏')
    })
  }

  onStar (row: any, g: any) {
    star(row.id, g.id).then(data => {
      Object.assign(row, { starred: true })
      this.$message.success('已收藏')
    })
  }

  // onShortcutPaste (text: string) {
  //   if (this.cpMvPasting) return
  //   const data = JSON.parse(atob(text))
  //   const type = data.type
  //   const sources = data.data || []
  //   const req = {
  //     sources: sources,
  //     target: this.current.id
  //   }
  //   this.cpMvPasting = true
  //   const handler = this.$message({
  //     message: `${type === 'move' ? '移动' : '复制'}中...`,
  //     type: 'info',
  //     duration: 0
  //   })
  //   const request = type === 'move' ? mv(req) : cp(req)
  //   request.then(() => {
  //     this.data.reload(true)
  //   }).finally(() => {
  //     handler.close()
  //     this.cpMvPasting = false
  //   })
  // }

  initShortcutEvent () {
    const container = this.$el as HTMLElement

    const checkAllHandler = (e: KeyboardEvent) => {
      if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        this.onCheckAll()
      }
    }
    const copyHandler = (e: KeyboardEvent) => {
      if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        this.onShortcutCopy('copy')
      }
    }
    const moveHandler = (e: KeyboardEvent) => {
      if (e.key === 'x' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        this.onShortcutCopy('move')
      }
    }
    container.addEventListener('keydown', checkAllHandler)
    container.addEventListener('keydown', copyHandler)
    container.addEventListener('keydown', moveHandler)

    return function destroy () {
      container.removeEventListener('keydown', checkAllHandler)
      container.removeEventListener('keydown', copyHandler)
      container.removeEventListener('keydown', moveHandler)
    }
  }

  loadStarGroups () {
    queryGroups().then(data => {
      this.starGroups = data || []
    })
  }

  mounted () {
    const fn = this.initShortcutEvent()
    this.$once('hook:beforeDestroy', function () {
      fn()
    })
  }

  created () {
    this.loadStarGroups()
  }

  $style!: any
}
</script>

<style lang="scss" module>
@import "~@/styles/vars.scss";

$col4-width: 36px;

.container {
  &:focus, &:active {
    outline: 0;
    background-color: rgba($color-primary-lighten-5, .3);
  }
}

.dragIcon {
  width: 48px;
  color: $color-primary;
  font-size: 32px;
  position: absolute;
  background-color: transparent;
  z-index: -1;
}

.iconBadge {
  position: absolute;
  width: 16px;
  padding: 2px 4px;
  top: 0;
  right: -12px;
  border-radius: 12px;
  background-color: $color-primary;
  color: #fff;
  font-size: 12px;
  text-align: center;
  line-height: 16px;
}

.list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.item {
  position: relative;
  display: flex;
  align-items: center;
  margin: 0 12px;
  border-bottom: 1px solid $border-split-color;
  height: 48px;
  padding: 8px 0;

  &:hover, &.checked, &.selected {
    background-color: $bg-body-color;
  }

  &:hover, &.selected {
    .actions {
      display: block;
    }
  }

  &:hover, &.checked {
    .col0 {
      visibility: visible;
    }
  }
}

.col {
  flex: 0 0 auto;
}

.col0 {
  width: 40px;
  padding-left: 8px;
  // background-color: cyan;
  visibility: hidden;
}

.col1 {
  width: 70%;
  min-width: 220px;
  flex: 1 1 auto;
  display: flex;
  // background-color: chartreuse;
}

.col2 {
  width: 15%;
  min-width: 220px;
  // background-color: chocolate;
}

.col3 {
  width: 10%;
  min-width: 60px;
  // background-color: cornflowerblue;
}

.col4 {
  width: $col4-width;
  padding-right: 12px;
  // background-color: cyan;
  text-align: right;
}

.actions {
  position: absolute;
  right: $col4-width;
  background-color: $bg-body-color;
  display: none;
}

.docIcon {
  flex: 0 0 auto;
  width: 48px;
  color: $color-primary;
  font-size: 32px;
}

.docContent {
  flex: 1 1 auto;
  width: 0;
}

.docText {
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  width: 100%;
}

.docTextContent {
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;

  &:hover {
    color: $color-primary;
    opacity: .8;
    cursor: pointer;
  }
}

.docTextAction {
  flex: 0 0 auto;
}
</style>
